package top.jacktgq.concurrent_utils.reentrant;

import top.jacktgq.utils.LogUtils;
import top.jacktgq.utils.Sleeper;

import java.util.concurrent.locks.StampedLock;

/**
 * @Author CandyWall
 * @Date 2020/11/5--23:18
 * @Description 使用测试StampedLock
 */
public class StampedLockDemo {
    public static void main(String[] args) {
        //demo1();
        demo2();
    }

    //读读并发
    private static void demo1() {
        DataContainerStamped dataContainerStamped = new DataContainerStamped(5);
        new Thread(() -> {
            dataContainerStamped.read(1);
        }, "t1").start();
        Sleeper.sleep(1);
        new Thread(() -> {
            dataContainerStamped.read(1);
        }, "t2").start();
    }

    //先读后写，读操作会采用乐观读锁执行，执行完之后释放了锁，写操作才能继续执行
    private static void demo2() {
        DataContainerStamped dataContainerStamped = new DataContainerStamped(5);
        new Thread(() -> {
            dataContainerStamped.read(0);
        }, "t1").start();
        Sleeper.sleep(1);
        new Thread(() -> {
            dataContainerStamped.write(1);
        }, "t2").start();
    }

    //先写后读，读操作必须等写操作先执行完释放锁，并且乐观读锁升级成读锁，乐观读锁不需要释放
    private static void demo3() {
        DataContainerStamped dataContainerStamped = new DataContainerStamped(5);
        new Thread(() -> {
            dataContainerStamped.write(1);
        }, "t1").start();
        Sleeper.sleep(1);
        new Thread(() -> {
            dataContainerStamped.read(1);
        }, "t2").start();
    }
}

class DataContainerStamped {
    private int data;
    private final StampedLock stampedLock = new StampedLock();

    public DataContainerStamped(int data) {
        this.data = data;
    }

    public int read(int readTime) {
        //尝试乐观读锁
        long stamp = stampedLock.tryOptimisticRead();
        LogUtils.log("DataContainerStamped", "optimistic read lock stamp: " + stamp);
        Sleeper.sleep(readTime);
        if (stampedLock.validate(stamp)) {
            LogUtils.log("DataContainerStamped", "read finish... stamp: " + stamp);
            return data;
        }

        //锁升级，从乐观读锁升级为读锁
        try {
            stamp = stampedLock.readLock();
            LogUtils.log("DataContainerStamped", "read lock stamp: " + stamp);
            Sleeper.sleep(readTime);
            LogUtils.log("DataContainerStamped", "read finish stamp: " + stamp);
            return data;
        } finally {
            LogUtils.log("DataContainerStamped", "read unlock stamp: " + stamp);
            stampedLock.unlockRead(stamp);
        }


    }

    public void write(int newData) {
        long stamp = stampedLock.writeLock();
        LogUtils.log("DataContainerStamped", "write lock stamp: " + stamp);

        try {
            Sleeper.sleep(1);
            this.data = newData;
        } finally {
            LogUtils.log("DataContainerStamped", "write unlock stamp: " + stamp);
            stampedLock.unlockWrite(stamp);
        }
    }
}
